﻿#if !WINDOWS_PHONE && !NETFX_CORE
using System.ComponentModel.Composition;
#endif
using System.Diagnostics.Contracts;

namespace DaveSexton.Labs
{
	/// <summary>
	/// Provides an entry point for running experiments in a controlled environment, for performing
	/// public integration and regression testing, and for encapsulating working examples that can
	/// serve as executable documentation.
	/// </summary>
	/// <remarks>
	/// <include file='Lab.xml' path='type[@id="Lab"]/remarks[@kind="intro"]/*'/>
	/// <include file='Lab.xml' path='property[@id="DisplayName"]/*'/>
	/// <include file='Lab.xml' path='property[@id="Description"]/*'/>
	/// <include file='Lab.xml' path='property[@id="Categories"]/*'/>
	/// <include file='Lab.xml' path='type[@id="Lab"]/remarks[@kind="remainder"]/*'/>
	/// </remarks>
#if !WINDOWS_PHONE && !NETFX_CORE
	[InheritedExport]		// Convention builder is used instead for NETFX_CORE
#endif
	[ContractClass(typeof(ILabContract))]
	public interface ILab
	{
		/// <summary>
		/// Gets or sets whether the lab should be included when it's discovered by a <see cref="LabController"/>.
		/// </summary>
		bool IsEnabled { get; set; }

		/// <summary>
		/// Gets the lab's source code.
		/// </summary>
		/// <remarks>
		/// <alert type="note">
		/// When implementing <see cref="SourceCode"/> in classes that do not derive from <see cref="Lab"/>, returning 
		/// <see langword="null" /> indicates to the infrastructure that the source code embedded in the library as a 
		/// resource via the <strong>Labs.targets</strong> file, if any, should be loaded into the <see cref="Proxy"/>
		/// when this lab is loaded.
		/// </alert>
		/// </remarks>
		/// <value>The lab's source code or <see langword="null"/> to have it automatically loaded from an embedded resource file.</value>
		string SourceCode { get; }

		/// <summary>
		/// Gets the lab's source XAML.
		/// </summary>
		/// <remarks>
		/// <alert type="note">
		/// When implementing <see cref="SourceXaml"/> in classes that do not derive from <see cref="Lab"/>, returning 
		/// <see langword="null" /> indicates to the infrastructure that the source XAML embedded in the library as a 
		/// resource via the <strong>Labs.targets</strong> file, if any, should be loaded into the <see cref="Proxy"/>
		/// when this lab is loaded.
		/// </alert>
		/// </remarks>
		/// <value>The lab's source XAML or <see langword="null"/> to have it automatically loaded from an embedded resource file.</value>
		string SourceXaml { get; }

		/// <summary>
		/// Gets or sets the <see cref="Lab"/> object that provides services for an <see cref="ILab"/> instance.
		/// </summary>
		/// <remarks>
		/// <para>
		/// The <see cref="Proxy"/> property is used as a workaround to the lack of support in C# for multiple inheritance 
		/// from base classes.  Multiple inheritance is required for special lab types such as WindowsLab, 
		/// which already derives from the <see cref="System.Windows.Controls.UserControl"/> class.  It cannot also derive 
		/// from <see cref="Lab"/>, so the <see cref="Proxy"/> is assigned to an instance of an internal type 
		/// that derives from <see cref="Lab"/>, which provides services to labs such as a 
		/// <see cref="System.Diagnostics.TraceSource"/> implementation, the retrieval of information about the lab 
		/// from reflection, and category aggregation.  Custom types that implement <see cref="ILab"/> do not have 
		/// to implement these services since they can just delegate the work to the <see cref="Proxy"/>, which is 
		/// automatically assigned by the <see cref="LabController"/> when the <see cref="Lab"/> is first loaded.
		/// </para>
		/// <alert type="note">
		/// An instance of <see cref="Lab"/> returns a reference to itself from the <see cref="Proxy"/> property.  It also 
		/// prevents assignments by throwing an exception.
		/// </alert>
		/// </remarks>
		Lab Proxy { get; set; }

		/// <summary>
		/// Gets a value indicating whether <see cref="Proxy"/> returns an object or <see langword="null"/>.
		/// </summary>
		bool HasProxy { get; }

		/// <summary>
		/// Gets whether the lab is a proxy for <see cref="ActualLab"/>.
		/// </summary>
		bool IsProxy { get; }

		/// <summary>
		/// Gets the actual <see cref="ILab"/> instance that this object represents.
		/// </summary>
		/// <remarks>
		/// The <see cref="ActualLab"/> property is useful for UI labs that derive from a control base
		/// and implement <see cref="ILab"/>.  Since the <see cref="Lab"/> class doesn't derive from 
		/// the control base, the UI lab host must be able to retrieve a reference to the underlying 
		/// <see cref="ILab"/> instance.
		/// </remarks>
		/// <value>If <see cref="IsProxy"/> is <see langword="true" />, then a reference to a different instance of <see cref="ILab"/> is returned; 
		/// otherwise, a reference to this object is returned.</value>
		ILab ActualLab { get; }

		/// <summary>
		/// Formats the specified source code or returns <see langword="null"/> to indicate that the default 
		/// formatting should be used.
		/// </summary>
		/// <remarks>
		/// <see cref="FormatSourceCode"/> provides <see cref="ILab"/> implementors an opportunity to 
		/// perform a one-time formatting on the source code that is retrieved from a resource file 
		/// when <see cref="SourceCode"/> returns <see langword="null"/>.  For example, you can replace
		/// all TAB characters with a fixed number of spaces.  To use the default formatting behavior, 
		/// return <see langword="null"/>.
		/// </remarks>
		/// <param name="source">The source code to be formatted.</param>
		/// <returns>Formatted source code or <see langword="null"/>.</returns>
		string FormatSourceCode(string source);

		/// <summary>
		/// Formats the specified XAML or returns <see langword="null"/> to indicate that the default 
		/// formatting should be used.
		/// </summary>
		/// <remarks>
		/// <see cref="FormatSourceCode"/> provides <see cref="ILab"/> implementors an opportunity to 
		/// perform a one-time formatting on the source code that is retrieved from the resource file 
		/// when <see cref="SourceCode"/> returns <see langword="null"/>.  For example, you can replace
		/// all TAB characters with a fixed number of spaces.  To use the default formatting behavior, 
		/// return <see langword="null"/>.
		/// </remarks>
		/// <param name="source">The XAML to be formatted.</param>
		/// <returns>Formatted XAML or <see langword="null"/>.</returns>
		string FormatSourceXaml(string source);
	}

	[ContractClassFor(typeof(ILab))]
	internal abstract class ILabContract : ILab
	{
		public bool IsEnabled
		{
			get;
			set;
		}

		public string SourceCode
		{
			get
			{
				return null;
			}
		}

		public string SourceXaml
		{
			get
			{
				return null;
			}
		}

		public Lab Proxy
		{
			get
			{
				Contract.Ensures(Contract.Result<Lab>() == null || Contract.Result<Lab>().ActualLab == this);
				return null;
			}
			set
			{
				Contract.Requires(!HasProxy);
				Contract.Requires(value != null);
				Contract.Requires(value.IsProxy);
				Contract.Requires(value.ActualLab == this);
			}
		}

		public bool HasProxy
		{
			get
			{
				Contract.Ensures(Contract.Result<bool>() == (Proxy != null));
				return false;
			}
		}

		public bool IsProxy
		{
			get
			{
				Contract.Ensures(Contract.Result<bool>() == (ActualLab != this));
				return false;
			}
		}

		public ILab ActualLab
		{
			get
			{
				Contract.Ensures(Contract.Result<ILab>() != null);
				return null;
			}
		}

		public string FormatSourceCode(string source)
		{
			Contract.Requires(!string.IsNullOrEmpty(source));
			return null;
		}

		public string FormatSourceXaml(string source)
		{
			Contract.Requires(!string.IsNullOrEmpty(source));
			return null;
		}
	}
}